#ifndef BASE_BASE_FILE_H
#define BASE_BASE_FILE_H

#include "base/macro_def.h"
#include <sys/uio.h>
#include <sys/mman.h>

class DiskFile {
  public:
    DiskFile(const std::string& path, int mode = O_CREAT | O_CLOEXEC | O_RDWR)
        : data_(NULL) {
      fd_ = ::open(path.c_str(), mode, 0644);
      CHECK_NE(fd_, -1)<< path;
    }

    explicit DiskFile(int fd)
    : fd_(fd), data_(NULL) {
    }

    static DiskFile* CreateDiskFile(int fd) {
      return new DiskFile(fd);
    }

    virtual ~DiskFile() {
      UnMap();
      if (fd_ != -1) {
        ::close(fd_);
        fd_ = -1;
      }
    }

    void Sync() {
      int ret = fsync(fd_);
      PCHECK(ret == 0) << "fsync error";
    }

    void Truncate(int32_t length) {
      if (length < 0) return;
      int ret = ::ftruncate(fd_, length);
      PCHECK(ret != -1) << "ftruncate error";
    }

    char* Mmap(int prot);

    const char* ReadOnlyMmap() {
      return Mmap(PROT_READ);
    }

    void UnMap() {
      if (data_ == NULL) return;
      ::munmap(data_, size());
    }

    ssize_t size() {
      struct stat st = {0};
      int ret = ::fstat(fd_, &st);
      CHECK_NE(ret, -1) << "fstat error, fd: " << fd_;
      return st.st_size;
    }

    bool empty() {
      return size() == 0;
    }

    bool Seek(int32_t offset, int whence) {
      int ret = ::lseek(fd_, offset, whence);
      if (ret == -1) {
        LOG(WARNING) << "lseek error, fd: " << fd_;
        return false;
      }
      return true;
    }

    int32_t Position() const {
      int32_t ret = ::lseek(fd_, 0, SEEK_CUR);
      if (ret == -1) {
        LOG(WARNING) << "lseek error, fd: " << fd_;
        return -1;
      }
      return ret;
    }

    int32_t Read(char* buf, int32_t size);
    int32_t Read(char* buf, int32_t size, int32_t offset);
    int32_t Read(struct iovec *iov, int32_t iovcnt);

    int32_t Write(const char* buf, int32_t size);
    int32_t Write(const char* buf, int32_t size, int32_t offset);
    void Write(const struct iovec* iov, int32_t iovcnt);

    template<typename Type>
    int32_t Read(Type* type) {
      return Read((char*) type, sizeof(Type));
    }

    template<typename Type>
    int32_t Write(const Type* type) {
      return Write((const char*) type, sizeof(Type));
    }

    void CopyFrom(DiskFile& rhs);

    private:
    int fd_;
    char* data_;

    DISALLOW_COPY_AND_ASSIGN(DiskFile);
  };

class ReadOnlyFile : public DiskFile {
  public:
    explicit ReadOnlyFile(const std::string& path)
        : DiskFile(path, O_RDONLY) {
    }
    virtual ~ReadOnlyFile() {
    }

  private:
    DISALLOW_COPY_AND_ASSIGN(ReadOnlyFile);
};

class AppendOnlyFile : public DiskFile {
  public:
    explicit AppendOnlyFile(const std::string& path)
        : DiskFile(path, O_WRONLY | O_CREAT) {
    }
    virtual ~AppendOnlyFile() {
    }

  private:
    DISALLOW_COPY_AND_ASSIGN(AppendOnlyFile);
};

#endif  // BASE_BASE_FILE_H_
